#pragma once
//========================================================================
// GameCode.h : Defines the entry point for the application.
//========================================================================

#include "Initialization.h"
#include "BaseGameLogic.h"
#include "SceneNodes.h"
#include "UserInterface.h"

class FontHandler;
class BaseUI;
class EventManager;
class LuaStateManager;
class BaseSocketManager;
class NetworkEventForwarder;
class HumanView;

//
// class GameApp
//
class GameApp
{
protected:

	HINSTANCE m_hInstance;				// the module instance
	bool m_windowedMode;				// true if the app is windowed, false if fullscreen
	bool m_isRunning;					// true if everything is initialized and the game is in the main loop
	bool m_quitRequested;				// true if the app should run the exit sequence
	bool m_quitting;					// true if the app is running the exit sequence
	RECT m_rcDesktop;					// current desktop size - not necessarilly the client window size
	Vector2 m_screenSize;				// game screen size
	int m_iColorDepth;					// current color depth (16 or 32)
	bool m_isEditorRunning;				// true if the game editor is running


public:
	const Vector2& GetScreenSize() {return m_screenSize;}

protected:
	std::map<std::wstring,std::wstring> m_textResource;
	std::map<std::wstring,UINT> m_hotkeys;

	int m_hasModalDialog;					// determines if a modal dialog is up
	int PumpUntilMessage (UINT msgEnd, WPARAM* p_wParam, LPARAM* p_lParam);
	int	EatSpecificMessages(UINT msgType, optional<LPARAM> lParam, optional<WPARAM> wParam);
	void FlashWhileMinimized();

public:

	GameApp();

	// Game Application Data
	// You must define these in an inherited
	// class - see TeapotWarsApp for an example
	virtual TCHAR* VGetGameTitle() = 0;
	virtual TCHAR* VGetGameAppDirectory() = 0;
	virtual HICON VGetIcon() = 0;

	// Win32 Specific Stuff
	HWND GetHwnd();
	HINSTANCE GetInstance() {return m_hInstance;}
	virtual bool InitInstance(HINSTANCE hInstance, LPWSTR lpCmdLine, HWND hWnd = NULL, int screenWidth = SCREEN_WIDTH, int screenHeight = SCREEN_HEIGHT);	

	static LRESULT CALLBACK MsgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* p_noFurtherProcessing, void* p_userContext);
	bool HasModalDialog() {return m_hasModalDialog != 0;}
	void ForceModalExit() {PostMessage(GetHwnd(), g_msgEndModal, 0, g_quitNoPrompt);}

	LRESULT OnDisplayChange(int colorDepth, int width, int height);
	LRESULT OnPowerBroadcast(int event);
	LRESULT OnSysCommand(WPARAM wParam, LPARAM lParam);
	LRESULT OnClose();

	// Game Application actions
	LRESULT OnAltEnter();
	LRESULT OnNcCreate(LPCREATESTRUCT cs);

	bool LoadStrings(std::string language);
	std::wstring GetString(std::wstring sID);				
	int GetHotKeyForString(std::wstring sID);
	UINT MapCharToKeycode(const char pHotkey);

	int Modal(boost::shared_ptr<IScreenElement> p_modalScreen, int defaultAnswer);

	// TODO: - Seems like this class could be refactored, especailly the renderer !

	enum Renderer
	{
		Renderer_Unknown,
		Renderer_D3D9,
		Renderer_D3D11
	};

	boost::shared_ptr<IRenderer> m_renderer;

	static Renderer GetRendererImpl();

	// DirectX9 Specific Stuff
	static bool CALLBACK IsD3D9DeviceAcceptable(D3DCAPS9* p_caps, D3DFORMAT adapterFormat, D3DFORMAT backBufferFormat, bool windowed, void* p_userContext);
	static HRESULT CALLBACK OnD3D9CreateDevice(IDirect3DDevice9* p_d3dDevice, const D3DSURFACE_DESC* p_backBufferSurfaceDesc, void* p_userContext);
	static HRESULT CALLBACK OnD3D9ResetDevice(IDirect3DDevice9* p_d3dDevice, const D3DSURFACE_DESC* p_backBufferSurfaceDesc, void* p_userContext);
	static void CALLBACK OnD3D9FrameRender(IDirect3DDevice9* p_d3dDevice, double time, float elapsedTime, void* p_userContext);
	static void CALLBACK OnD3D9LostDevice(void* p_userContext);
	static void CALLBACK OnD3D9DestroyDevice(void* p_userContext);

	// DirectX 11 Specific Stuff
	static bool CALLBACK IsD3D11DeviceAcceptable(const CD3D11EnumAdapterInfo* p_adapterInfo, UINT output, const CD3D11EnumDeviceInfo* p_deviceInfo, DXGI_FORMAT backBufferFormat, bool windowed, void* p_userContext);
	static HRESULT CALLBACK OnD3D11CreateDevice(ID3D11Device* p_d3dDevice, const DXGI_SURFACE_DESC* p_backBufferSurfaceDesc, void* p_userContext);
	static HRESULT CALLBACK OnD3D11ResizedSwapChain(ID3D11Device* p_d3dDevice, IDXGISwapChain* p_swapChain, const DXGI_SURFACE_DESC* p_backBufferSurfaceDesc, void* p_userContext);
	static void CALLBACK OnD3D11ReleasingSwapChain(void* p_userContext);
	static void CALLBACK OnD3D11DestroyDevice(void* p_userContext);
	static void CALLBACK OnD3D11FrameRender(ID3D11Device* p_d3dDevice, ID3D11DeviceContext* p_d3dImmediateContext, double time, float elapsedTime, void* p_userContext);

	static bool CALLBACK ModifyDeviceSettings(DXUTDeviceSettings* p_deviceSettings, void* p_userContext);
	static void CALLBACK OnUpdateGame(double time, float elapsedTime, void *p_userContext);


	// GameCode Specific Stuff
	BaseGameLogic *mp_game;
	struct GameOptions m_options;

	// You must define these functions to initialize your game.
	// Look for them in TeapotWars.h and TeapotWars.cpp
	virtual BaseGameLogic* VCreateGameAndView() = 0;
	virtual bool VLoadGame();
	HumanView* GetHumanView();				// Added post press - it was convenient to grab the HumanView attached to the game.

	// File and Resource System
	class ResourceCache* mp_resourceCache;
	TCHAR m_saveGameDirectory[MAX_PATH];

	bool IsEditorRunning() {return m_isEditorRunning;}

	// Event manager
	EventManager *mp_eventManager;

	// Socket manager - could be server or client
	BaseSocketManager *mp_baseSocketManager;
    NetworkEventForwarder* mp_networkEventForwarder;
	bool AttachAsClient();

protected:
    virtual void VCreateNetworkEventForwarder();
    virtual void VDestroyNetworkEventForwarder();

public:

	// Main loop processing
	void AbortGame() {m_quitting = true;}
	int GetExitCode() {return DXUTGetExitCode();}
	bool IsRunning() {return m_isRunning;}
	void SetQuitting(bool quitting) {m_quitting = quitting;}

    BaseGameLogic* GetGameLogic() const {return mp_game;}

protected:
    virtual void VRegisterGameEvents(){}

private:
    void RegisterEngineEvents();
};

extern GameApp *gp_app;